/*********************************************************************
 * Copyright (C) 2002 Andrew Khan
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package jxl.biff;

import jxl.WorkbookSettings;
import jxl.common.Assert;
import jxl.common.Logger;
import jxl.format.Colour;
import jxl.format.Font;
import jxl.format.ScriptStyle;
import jxl.format.UnderlineStyle;
import jxl.read.biff.Record;

/**
 * A record containing the necessary data for the font information
 */
public class FontRecord extends WritableRecordData implements Font {
    /**
     * The logger
     */
    private static Logger logger = Logger.getLogger(FontRecord.class);

    /**
     * The point height of this font
     */
    private int pointHeight;
    /**
     * The index into the colour palette
     */
    private int colourIndex;
    /**
     * The bold weight for this font (normal or bold)
     */
    private int boldWeight;
    /**
     * The style of the script (italic or normal)
     */
    private int scriptStyle;
    /**
     * The underline style for this font (none, single, double etc)
     */
    private int underlineStyle;
    /**
     * The font family
     */
    private byte fontFamily;
    /**
     * The character set
     */
    private byte characterSet;

    /**
     * Indicates whether or not this font is italic
     */
    private boolean italic;
    /**
     * Indicates whether or not this font is struck out
     */
    private boolean struckout;
    /**
     * The name of this font
     */
    private String name;
    /**
     * Flag to indicate whether the derived data (such as the font index) has
     * been initialized or not
     */
    private boolean initialized;

    /**
     * The index of this font in the font list
     */
    private int fontIndex;

    /**
     * Dummy indicators for overloading the constructor
     */
    private static class Biff7 {
    }

    ;
    public static final Biff7 biff7 = new Biff7();

    /**
     * The conversion factor between microsoft internal units and point size
     */
    private static final int EXCEL_UNITS_PER_POINT = 20;

    /**
     * Constructor, used when creating a new font for writing out.
     *
     * @param bold the bold indicator
     * @param ps the point size
     * @param us the underline style
     * @param fn the name
     * @param it italicised indicator
     * @param ss the script style
     * @param ci the colour index
     */
    protected FontRecord(String fn, int ps, int bold, boolean it,
                         int us, int ci, int ss) {
        super(Type.FONT);
        boldWeight = bold;
        underlineStyle = us;
        name = fn;
        pointHeight = ps;
        italic = it;
        scriptStyle = ss;
        colourIndex = ci;
        initialized = false;
        struckout = false;
    }

    /**
     * Constructs this object from the raw data.  Used when reading in a
     * format record
     *
     * @param t the raw data
     * @param ws the workbook settings
     */
    public FontRecord(Record t, WorkbookSettings ws) {
        super(t);

        byte[] data = getRecord().getData();

        pointHeight = IntegerHelper.getInt(data[0], data[1]) /
                EXCEL_UNITS_PER_POINT;
        colourIndex = IntegerHelper.getInt(data[4], data[5]);
        boldWeight = IntegerHelper.getInt(data[6], data[7]);
        scriptStyle = IntegerHelper.getInt(data[8], data[9]);
        underlineStyle = data[10];
        fontFamily = data[11];
        characterSet = data[12];
        initialized = false;

        if ((data[2] & 0x02) != 0) {
            italic = true;
        }

        if ((data[2] & 0x08) != 0) {
            struckout = true;
        }

        int numChars = data[14];
        if (data[15] == 0) {
            name = StringHelper.getString(data, numChars, 16, ws);
        } else if (data[15] == 1) {
            name = StringHelper.getUnicodeString(data, numChars, 16);
        } else {
            // Some font names don't have the unicode indicator
            name = StringHelper.getString(data, numChars, 15, ws);
        }
    }

    /**
     * Constructs this object from the raw data.  Used when reading in a
     * format record
     *
     * @param t the raw data
     * @param ws the workbook settings
     * @param dummy dummy overload
     */
    public FontRecord(Record t, WorkbookSettings ws, Biff7 dummy) {
        super(t);

        byte[] data = getRecord().getData();

        pointHeight = IntegerHelper.getInt(data[0], data[1]) /
                EXCEL_UNITS_PER_POINT;
        colourIndex = IntegerHelper.getInt(data[4], data[5]);
        boldWeight = IntegerHelper.getInt(data[6], data[7]);
        scriptStyle = IntegerHelper.getInt(data[8], data[9]);
        underlineStyle = data[10];
        fontFamily = data[11];
        initialized = false;

        if ((data[2] & 0x02) != 0) {
            italic = true;
        }

        if ((data[2] & 0x08) != 0) {
            struckout = true;
        }

        int numChars = data[14];
        name = StringHelper.getString(data, numChars, 15, ws);
    }

    /**
     * Publicly available copy constructor
     *
     * @param  f the font to copy
     */
    protected FontRecord(Font f) {
        super(Type.FONT);

        Assert.verify(f != null);

        pointHeight = f.getPointSize();
        colourIndex = f.getColour().getValue();
        boldWeight = f.getBoldWeight();
        scriptStyle = f.getScriptStyle().getValue();
        underlineStyle = f.getUnderlineStyle().getValue();
        italic = f.isItalic();
        name = f.getName();
        struckout = f.isStruckout();
        initialized = false;
    }

    /**
     * Gets the byte data for writing out
     *
     * @return the raw data
     */
    public byte[] getData() {
        byte[] data = new byte[16 + name.length() * 2];

        // Excel expects font heights in 1/20ths of a point
        IntegerHelper.getTwoBytes(pointHeight * EXCEL_UNITS_PER_POINT, data, 0);

        // Set the font attributes to be zero for now
        if (italic) {
            data[2] |= 0x2;
        }

        if (struckout) {
            data[2] |= 0x08;
        }

        // Set the index to the colour palette
        IntegerHelper.getTwoBytes(colourIndex, data, 4);

        // Bold style
        IntegerHelper.getTwoBytes(boldWeight, data, 6);

        // Script style
        IntegerHelper.getTwoBytes(scriptStyle, data, 8);

        // Underline style
        data[10] = (byte) underlineStyle;

        // Set the font family to be 0
        data[11] = fontFamily;

        // Set the character set to be zero
        data[12] = characterSet;

        // Set the reserved bit to be zero
        data[13] = 0;

        // Set the length of the font name
        data[14] = (byte) name.length();

        data[15] = (byte) 1;

        // Copy in the string
        StringHelper.getUnicodeBytes(name, data, 16);

        return data;
    }

    /**
     * Accessor to see whether this object is initialized or not.
     *
     * @return TRUE if this font record has been initialized, FALSE otherwise
     */
    public final boolean isInitialized() {
        return initialized;
    }

    /**
     * Sets the font index of this record.  Called from the FormattingRecords
     *  object
     *
     * @param pos the position of this font in the workbooks font list
     */
    public final void initialize(int pos) {
        fontIndex = pos;
        initialized = true;
    }

    /**
     * Resets the initialize flag.  This is called by the constructor of
     * WritableWorkbookImpl to reset the statically declared fonts
     */
    public final void uninitialize() {
        initialized = false;
    }

    /**
     * Accessor for the font index
     *
     * @return the font index
     */
    public final int getFontIndex() {
        return fontIndex;
    }

    /**
     * Sets the point size for this font, if the font hasn't been initialized
     *
     * @param ps the point size
     */
    protected void setFontPointSize(int ps) {
        Assert.verify(!initialized);

        pointHeight = ps;
    }

    /**
     * Gets the point size for this font, if the font hasn't been initialized
     *
     * @return the point size
     */
    public int getPointSize() {
        return pointHeight;
    }

    /**
     * Sets the bold style for this font, if the font hasn't been initialized
     *
     * @param bs the bold style
     */
    protected void setFontBoldStyle(int bs) {
        Assert.verify(!initialized);

        boldWeight = bs;
    }

    /**
     * Gets the bold weight for this font
     *
     * @return the bold weight for this font
     */
    public int getBoldWeight() {
        return boldWeight;
    }

    /**
     * Sets the italic indicator for this font, if the font hasn't been
     * initialized
     *
     * @param i the italic flag
     */
    protected void setFontItalic(boolean i) {
        Assert.verify(!initialized);

        italic = i;
    }

    /**
     * Returns the italic flag
     *
     * @return TRUE if this font is italic, FALSE otherwise
     */
    public boolean isItalic() {
        return italic;
    }

    /**
     * Sets the underline style for this font, if the font hasn't been
     * initialized
     *
     * @param us the underline style
     */
    protected void setFontUnderlineStyle(int us) {
        Assert.verify(!initialized);

        underlineStyle = us;
    }

    /**
     * Gets the underline style for this font
     *
     * @return the underline style
     */
    public UnderlineStyle getUnderlineStyle() {
        return UnderlineStyle.getStyle(underlineStyle);
    }

    /**
     * Sets the colour for this font, if the font hasn't been
     * initialized
     *
     * @param c the colour
     */
    protected void setFontColour(int c) {
        Assert.verify(!initialized);

        colourIndex = c;
    }

    /**
     * Gets the colour for this font
     *
     * @return the colour
     */
    public Colour getColour() {
        return Colour.getInternalColour(colourIndex);
    }

    /**
     * Sets the script style (eg. superscript, subscript) for this font,
     * if the font hasn't been initialized
     *
     * @param ss the colour
     */
    protected void setFontScriptStyle(int ss) {
        Assert.verify(!initialized);

        scriptStyle = ss;
    }

    /**
     * Gets the script style
     *
     * @return the script style
     */
    public ScriptStyle getScriptStyle() {
        return ScriptStyle.getStyle(scriptStyle);
    }

    /**
     * Gets the name of this font
     *
     * @return the name of this font
     */
    public String getName() {
        return name;
    }

    /**
     * Standard hash code method
     * @return the hash code for this object
     */
    public int hashCode() {
        return name.hashCode();
    }

    /**
     * Standard equals method
     * @param o the object to compare
     * @return TRUE if the objects are equal, FALSE otherwise
     */
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof FontRecord)) {
            return false;
        }

        FontRecord font = (FontRecord) o;

        if (pointHeight == font.pointHeight &&
                colourIndex == font.colourIndex &&
                boldWeight == font.boldWeight &&
                scriptStyle == font.scriptStyle &&
                underlineStyle == font.underlineStyle &&
                italic == font.italic &&
                struckout == font.struckout &&
                fontFamily == font.fontFamily &&
                characterSet == font.characterSet &&
                name.equals(font.name)) {
            return true;
        }

        return false;
    }

    /**
     * Accessor for the strike out flag
     *
     * @return TRUE if this font is struck out, FALSE otherwise
     */
    public boolean isStruckout() {
        return struckout;
    }

    /**
     * Sets the struck out flag
     *
     * @param os TRUE if the font is struck out, false otherwise
     */
    protected void setFontStruckout(boolean os) {
        struckout = os;
    }
}


